﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="TradeSeries.cs" company="">
//   
// </copyright>
// <summary>
//   The trade series.
// </summary>
// --------------------------------------------------------------------------------------------------------------------


using System;
using System.Collections.Generic;
using System.Linq;

namespace Systemathics.FrameWork
{
    [Serializable]
    public sealed class TradeSeries : Series<Trade>, ICloneable
    {
        public event EventHandler<FireMessageEventArgs> EmitSeriesError;

        #region Constructors

        public TradeSeries(string name) : base(name)
        {
            EmitBaseSeriesError += TradeSeriesEmitSeriesError;
        }
        public TradeSeries(string name, List<Trade> trades) : base(name, trades)
        {
            EmitBaseSeriesError += TradeSeriesEmitSeriesError;
        }

        #endregion Constructors

        #region TradeSeries Methods

        public Trade this[DateTime dateTime]
        {
            get
            {
                try
                {
                    lock (this)
                    {
                        return series.Find(b => b.Time == dateTime);
                    }
                }
                catch (Exception ex)
                {
                    if (EmitSeriesError != null)
                        EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                    return null;
                }
            }
        }
        private void TradeSeriesEmitSeriesError(object sender, FireMessageEventArgs e)
        {
            if (EmitSeriesError != null)
                EmitSeriesError(sender, e);
        }

        public Decimal High()
        {
            try
            {
                lock (this)
                {
                    return series.Max(b => b.Price);
                }
            }
            catch (Exception ex)
            {
                if (EmitSeriesError != null)
                    EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                return Decimal.Zero;
            }
        }
        public Decimal High(int nElements)
        {
            try
            {
                lock (this)
                {
                    int N = series.Count;
                    List<Trade> tempList = series.GetRange(N - nElements, nElements);
                    return tempList.Max(b => b.Price);
                }
            }
            catch (Exception ex)
            {
                if (EmitSeriesError != null)
                    EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                return Decimal.Zero;
            }
        }
        public Decimal High(DateTime fromThisDate, DateTime toThisDate)
        {
            if (toThisDate > fromThisDate)
            {
                try
                {
                    lock (this)
                    {
                        var start = (int) GetIndex(fromThisDate);
                        var end = (int) GetIndex(toThisDate);
                        List<Trade> tempList = series.GetRange(start, end - start);
                        return tempList.Max(b => b.Price);
                    }
                }
                catch (Exception ex)
                {
                    if (EmitSeriesError != null)
                        EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                    return Decimal.Zero;
                }
            }
            return 0m;
        }
        public Decimal High(int start, int end)
        {
            if (series.Count >= end && end >= start)
            {
                try
                {
                    lock (this)
                    {
                        List<Trade> tempList = series.GetRange(start, end - start);
                        return tempList.Max(b => b.Price);
                    }
                }
                catch (Exception ex)
                {
                    if (EmitSeriesError != null)
                        EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                    return Decimal.Zero;
                }
            }
            return 0m;
        }
        public Decimal Low()
        {
            if (series.Count > 0)
                lock (this)
                {
                    return series.Min(b =>  b.Price);
                }
            return Decimal.Zero;
        }
        public Decimal Low(int nElements)
        {
            try
            {
                lock (this)
                {
                    int N = series.Count;
                    List<Trade> TempList = series.GetRange(N - nElements, nElements);
                    return TempList.Min(b =>  b.Price);
                }
            }
            catch (Exception ex)
            {
                if (EmitSeriesError != null)
                    EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                return Decimal.Zero;
            }
        }
        public Decimal Low(DateTime FromThisDate, DateTime ToThisDate)
        {
            if (ToThisDate > FromThisDate)
            {
                try
                {
                    lock (this)
                    {
                        var start = (int) GetIndex(FromThisDate);
                        var end = (int) GetIndex(ToThisDate);
                        List<Trade> TempList = series.GetRange(start, end - start);
                        return TempList.Min(b =>  b.Price);
                    }
                }
                catch (Exception ex)
                {
                    if (EmitSeriesError != null)
                        EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                    return Decimal.Zero;
                }
            }
            return Decimal.Zero;
        }
        public Decimal Low(int start, int end)
        {
            if (series.Count >= end && end >= start)
            {
                try
                {
                    lock (this)
                    {
                        List<Trade> TempList = series.GetRange(start, end - start);
                        return TempList.Min(b =>  b.Price);
                    }
                }
                catch (Exception ex)
                {
                    if (EmitSeriesError != null)
                        EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                    return Decimal.Zero;
                }
            }
            return Decimal.Zero;
        }
        public void Remove(DateTime DateTime)
        {
            lock (this)
            {
                if (Contains(DateTime))
                    series.RemoveAt((int) GetIndex(DateTime));
            }
        }
        public bool Contains(DateTime dateTime)
        {
            try
            {
                if (series.Exists(delegate(Trade b) { return b.Time == dateTime; }))
                    return true;
                return false;
            }
            catch (Exception ex)
            {
                if (EmitSeriesError != null)
                    EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                return false;
            }
        }
        public DateTime? GetDateTime(int index)
        {
            DateTime? result = null;
            try
            {
                lock (this)
                {
                    Trade b = series.ElementAt(index);
                    result = (DateTime) b.Time;
                }
            }
            catch (Exception ex)
            {
                if (EmitSeriesError != null)
                    EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
            }

            return result;
        }
        public int? GetIndex(DateTime dateTime)
        {
            int? result = null;
            try
            {
                lock (this)
                {
                    if (Contains(dateTime))
                        result = (int) series.IndexOf(series.Find(delegate(Trade b) { return b.Time == dateTime; }));
                }
            }
            catch (Exception ex)
            {
                if (EmitSeriesError != null)
                    EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
            }

            return result;
        }
        public int Volume(int start, int end)
        {
            var size = 0;
            if (series.Count >= end && end >= start)
            {
                try
                {
                    lock (this)
                    {
                        List<Trade> TempList = series.GetRange(start, end - start);
                        foreach (Trade t in TempList)
                            size += (int) t.Size;
                        return size;
                    }
                }
                catch (Exception ex)
                {
                    if (EmitSeriesError != null)
                        EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                    return size;
                }
            }
            return size;
        }
        public BarSeries ToBarSeries(BarType bt, int value)
        {
            var Bars = new BarSeries(Ticker);
            if (Count > 2)
            {
                lock (this)
                {
                    Bars.BarType = bt;
                    Bars.BarSize = value;
                    try
                    {
                        switch (bt)
                        {
                            case BarType.Seconds:
                                var MarkUpDate = this[0].Time;
                                int MarkUpIndex = 0, Index = 0;

                                var span = new TimeSpan(0, 0, value);
                                if (span > (Last().Time - First().Time))
                                    return Bars;
                                foreach (Trade t in this)
                                {
                                    Index++;
                                    if (t.Time - MarkUpDate >= span)
                                    {
                                        var BeginTime = this[MarkUpIndex].Time;
                                        var EndTime = this[Index].Time;
                                        var Open = this[MarkUpIndex].Price;
                                        var High = this.High(MarkUpIndex, Index);
                                        var Low = this.Low(MarkUpIndex, Index);
                                        var Close = this[Index].Price;
                                        int volume = Volume(MarkUpIndex, Index);

                                        var b = new Bar(BeginTime, EndTime, Open, High, Low, Close, value, volume, 0);
                                        Bars.Add(b);

                                        MarkUpDate = EndTime;
                                        MarkUpIndex = Index;
                                    }
                                }

                                break;
                            case BarType.Ticks:
                                break;
                            case BarType.Volume:
                                break;
                            default:
                                break;
                        }
                    }
                    catch (Exception ex)
                    {
                        if (EmitSeriesError != null)
                            EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
                    }
                }
            }

            return Bars;
        }
        public Cross Crosses(TimeSeries ts)
        {
            try
            {
                lock (this)
                {
                    if (ts.Count() > 1 && Count > 1)
                    {
                        if (Ago(1).Price < ts.Ago(1) && Last().Price > ts.Ago(1))
                            return Cross.Above;
                        if (Ago(1).Price > ts.Ago(1) && Last().Price < ts.Ago(1))
                            return Cross.Below;
                    }
                }
            }
            catch (Exception ex)
            {
                if (EmitSeriesError != null) EmitSeriesError(this, new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
            }

            return Cross.None;
        }

        #endregion TradeSeries Methods

        #region ICloneable Members

        object ICloneable.Clone()
        {
            return MemberwiseClone();
        }

        #endregion
    }
} 